package com.atguigu.tingshu.order.service.impl;

import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import zipkin2.reporter.amqp.RabbitMQSender;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RefreshScope
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;


    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 订单确认数据汇总
     *
     * @param userId  用户ID
     * @param tradeVo 选择购买商品交易vo对象
     * @return
     */
    @Override
    public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
        //1.初始化OrderInfoVo
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //1.1. 初始化相关金额：0.00 此时金额必须是字符串"0.00"
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");

        //1.2. 初始化商品及优惠列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        //2.处理购买商品类型为：VIP套餐
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.1 远程调用“用户”服务获取套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "选择套餐：{}有误", tradeVo.getItemId());
            //2.2 封装价格：原价 减免价 定价价
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            if (orderAmount.compareTo(originalAmount) != 0) {
                //减免价格=原价-订单价
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //2.3 封装订单中商品明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("会员：" + vipServiceConfig.getName());
            orderDetailVo.setItemPrice(vipServiceConfig.getPrice());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVoList.add(orderDetailVo);
            //2.4 封装商品优惠列表
            if (orderAmount.compareTo(originalAmount) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("会员限时优惠：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3. 处理购买商品类型为：专辑
            //3.1 远程调用“用户”服务是否已购专辑
            Long albumId = tradeVo.getItemId();
            Boolean isPaid = userFeignClient.isPaidAlbum(albumId).getData();
            if (isPaid) {
                throw new RuntimeException("用户已购买专辑：" + albumId);
            }
            //3.2 远程调用"专辑"服务获取专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑：{}不存在", albumId);

            //3.3 远程调用用户服务获取用户身份，封装"商品"相关价格
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Boolean isVIP = false;
            if (1 == userInfoVo.getIsVip().intValue() && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            originalAmount = albumInfo.getPrice();
            orderAmount = originalAmount;
            //减免价=原价*折扣
            if (!isVIP && albumInfo.getDiscount().intValue() != -1) {
                //普通用户 且 专辑有普通用户折扣
                orderAmount = originalAmount.multiply(albumInfo.getDiscount()).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }

            if (isVIP && albumInfo.getVipDiscount().intValue() != -1) {
                //VIP用户 且 专辑有VIP用户折扣
                orderAmount = originalAmount.multiply(albumInfo.getVipDiscount()).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            if (orderAmount.compareTo(originalAmount) != 0) {
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //3.4 封装"商品"列表及商品优惠列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            if (orderAmount.compareTo(originalAmount) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑限时优惠：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.TODO 处理购买商品类型为：声音
            Long trackId = tradeVo.getItemId();
            Integer trackCount = tradeVo.getTrackCount();
            //4.1 远程调用"专辑服务"获取待购买声音列表
            List<TrackInfo> waitBuyTrackList = albumFeignClient.getWaitBuyTrackList(trackId, trackCount).getData();
            Assert.notNull(waitBuyTrackList, "不存在待结算声音");

            //4.2 远程调用专辑服务得到专辑信息-得到声音单价
            Long albumId = waitBuyTrackList.get(0).getAlbumId();
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑：{}不存在", albumId);

            //4.3 封装"商品"相关价格 声音单价*列表长度 注意：声音不支持折扣
            BigDecimal price = albumInfo.getPrice();
            originalAmount = price.multiply(BigDecimal.valueOf(waitBuyTrackList.size()));
            orderAmount = originalAmount;

            //4.4 封装"商品"列表 将集合泛型从TrackInfo转为OrderDetailVo
            orderDetailVoList = waitBuyTrackList
                    .stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音：" + trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());
        }
        //5.封装共有订单VO信息：价格、商品及优惠 TODO 、杂项信息(购买项目类型、流水号、时间戳签名)
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        orderInfoVo.setItemType(itemType);

        //5.1 为本次订单产生唯一流水号且存入Redis，将来在提交订单验证流水号
        String tradeNo = IdUtil.randomUUID();
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        //5.2 生成时间戳及订单签名
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        //5.2.1 将订单VO转为Map TODO:将付款方式payWay空值属性排除掉 不需要参与签名计算
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        //5.2.2 调用工具类生成签名值
        String sign = SignHelper.getSign(orderInfoMap);
        orderInfoVo.setSign(sign);

        //6.响应订单VO对象
        return orderInfoVo;
    }

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private RabbitService rabbitService;


    //获取Nacos中配置的订单超时时间
    @Value("${order.cancel}")
    private int ttl;


    /**
     * 提交订单
     *
     * @param userId      用户ID
     * @param orderInfoVo 订单vo信息
     * @return {orderNo:"订单编号"}
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //1.业务校验：验证流水号是否一致 避免用户误触回退按钮导致订单重复提交
        //1.1 确保判断流水号删除流水号原子性采用lua脚本来实现
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.2 创建Lua脚本
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            throw new GuiguException(500, "流水号校验失败请重试");
        }
        //2.业务校验：验证签名是否一致 避免用户篡改订单vo中参数导致商户利益损害
        //2.1 将提交订单VO转为Map用于验证签名
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo);
        //2.2 将Map中的payWay属性去除 在订单确认页生成签名没有加入payWay属性
        orderInfoMap.remove("payWay");
        SignHelper.checkSign(orderInfoMap);
        //3.核心业务1：保存订单相关信息（订单、订单明细、订单减免） 订单状态：未支付
        OrderInfo orderInfo = this.saveOrderInfo(userId, orderInfoVo);

        //4.判断付款方式-如果是账户余额付款
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
            //4.1.核心业务2：余额扣减
            //4.1.1 构建扣减余额VO对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            //4.1.2 远程调用账户服务扣减余额 一定要判断Feign调用结果业务状态码
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            if (!result.getCode().equals(200)) {
                throw new GuiguException(500, "账户余额不足");
            }

            //修改订单状态：已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //4.2.核心业务3：虚拟物品发货
            //4.2.1 构建虚拟物品发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //4.2.2 远程调用用户服务虚拟物品发货 一定要判断Feign调用结果业务状态码
            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!result.getCode().equals(200)) {
                throw new GuiguException(500, "虚拟物品发货异常");
            }
        }
        //5.TODO 判断付款方式-如果是微信付款，采用延迟消息完成延迟关单
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(orderInfoVo.getPayWay())) {
            rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), ttl);
        }
        Map<String, String> map = new HashMap<>();
        map.put("orderNo", orderInfo.getOrderNo());
        return map;
    }


    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    /**
     * 保存订单相关信息（订单、订单明细、订单减免）
     *
     * @param userId
     * @param orderInfoVo
     * @return
     */
    @Override
    public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {
        //1.保存订单信息
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //1.1 设置订单状态设置为：未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //1.2 设置用户ID
        orderInfo.setUserId(userId);
        //1.3 设置订单名称（从订单项获取名称）
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
        }
        //1.4 设置订单编号（确保全局唯一用于对接支付平台） 年月日+雪花算法
        String oderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(oderNo);
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();
        //2.保存订单明细列表
        //2.1 获取订单明细列表
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                //2.1 关联订单
                orderDetail.setOrderId(orderId);
                //2.2 保存订单明细
                orderDetailMapper.insert(orderDetail);
            }
        }
        //3.保存订单减免列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                //3.1 关联订单
                orderDerate.setOrderId(orderId);
                //3.2 保存订单减免
                orderDerateMapper.insert(orderDerate);
            }
        }
        return orderInfo;
    }

    /**
     * 根据订单编号查询订单信息(包含商品及商品优惠)
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        Assert.notNull(orderInfo, "订单不存在");
        //2.根据订单编号查询订单明细
        LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId, orderInfo.getId());
        List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailLambdaQueryWrapper);
        if (CollectionUtil.isNotEmpty(orderDetailList)) {
            orderInfo.setOrderDetailList(orderDetailList);
        }
        //3.根据订单编号查询订单减免明细
        LambdaQueryWrapper<OrderDerate> orderDerateLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderDerateLambdaQueryWrapper.eq(OrderDerate::getOrderId, orderInfo.getId());
        List<OrderDerate> orderDerateList = orderDerateMapper.selectList(orderDerateLambdaQueryWrapper);
        if (CollectionUtil.isNotEmpty(orderDerateList)) {
            orderInfo.setOrderDerateList(orderDerateList);
        }
        return orderInfo;
    }

    /**
     * 根据用户ID分页查询订单信息
     *
     * @param pageInfo
     * @param userId
     */
    @Override
    public void getUserOrderByPage(Page<OrderInfo> pageInfo, Long userId) {
        orderInfoMapper.getUserOrderByPage(pageInfo, userId);
    }

    /**
     * 查询并且根据状态取消订单
     *
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(Long orderId) {
        //1.根据订单ID查询订单信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //2.判读支付状态 ：如果是未支付，则取消订单
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
            //2.1 设置订单状态为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //2.2 更新订单信息
            orderInfoMapper.updateById(orderInfo);
        }
    }

    /**
     * 用户付款成功后，处理订单相关业务
     *
     * @param orderNo
     * @return
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.修改订单状态改为已支付
        OrderInfo orderInfo = this.getOrderInfo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfoMapper.updateById(orderInfo);

        //2.远程调用用户服务完成虚拟物品发货
        //2.1 构建虚拟物品发货VO对象
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        //2.1.1 购买项目类型：付款项目类型: 1001-专辑 1002-声音 1003-vip会员'
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        //2.1.2 从订单中获取订单明细得到购买项目ID列表
        List<OrderDetail> orderInfoOrderDetailList = orderInfo.getOrderDetailList();
        if (CollectionUtil.isNotEmpty(orderInfoOrderDetailList)) {
            List<Long> itemIdList = orderInfoOrderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //2.2 判断业务状态码
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!result.getCode().equals(200)) {
                throw new RuntimeException("远程调用-虚拟物品发货异常");
            }
        }
    }
}
